package zqwl_1dxrr16

import (
	"errors"
	"github.com/goburrow/modbus"
	"github.com/kataras/iris/v12"
	"github.com/zing-dev/go-bit-bytes/bin"
	"github.com/zing-dev/go-bit-bytes/bit"
	"sync"
	"time"
)

type modbusRTUClient struct {
	handler *modbus.RTUClientHandler
	client  modbus.Client
	locker  sync.Mutex
}

type modbusRTUApp struct {
	client *modbusRTUClient
	web    *iris.Application
	config Config
}

func NewRTUApp(config Config) App {

	if config.WebPort == 0 {
		config.WebPort = 5841
	}

	if config.SlaveId == 0 {
		config.SlaveId = 1
	}

	if config.Address == "" {
		config.Address = "COM19"
	}

	if config.ResetTime == 0 {
		config.ResetTime = 10
	}

	return &modbusRTUApp{
		web:    iris.New(),
		config: config,
	}
}

func (m *modbusRTUApp) GetRelay() Relay {
	return m.client
}

func (m *modbusRTUApp) GetWeb() *iris.Application {
	return m.web
}

func (m *modbusRTUApp) Run() error {
	relay, err := NewRTUClientHandler(m.config)
	if err != nil {
		return err
	}
	InitWeb(relay, &m.config)
	return err
}

func NewRTUClientHandler(config Config) (Relay, error) {
	if len(config.Address) > 0 && config.Address[0] != '\\' {
		config.Address = "\\\\.\\" + config.Address
	}
	handler := modbus.NewRTUClientHandler(config.Address)
	if config.SlaveId == 0 {
		config.SlaveId = 1
	}
	if config.BaudRate == 0 {
		config.BaudRate = 115200
	}
	if config.Parity == "" {
		config.Parity = "N"
	}
	handler.BaudRate = config.BaudRate
	handler.SlaveId = config.SlaveId
	handler.Parity = config.Parity
	handler.DataBits = config.DataBits
	handler.StopBits = config.StopBits
	handler.Timeout = time.Second * 3
	client := modbus.NewClient(handler)
	rtuClient := &modbusRTUClient{
		handler: handler,
		client:  client,
	}
	_, err := rtuClient.Info()
	if err != nil {
		return nil, err
	}
	return rtuClient, nil
}

func (m *modbusRTUClient) OnAll() error {
	m.locker.Lock()
	defer m.locker.Unlock()
	results, err := m.client.WriteMultipleCoils(0, 16, []byte{0xff, 0xff})
	if err != nil {
		return err
	}
	if len(results) == 2 {
		return nil
	}
	return ErrDateLength
}

// On
//线圈状态（高字节）为 0XFF 时，对应的继电器常开触点闭合，常闭触点断开；
//线圈状态（高字节）为 0X00 时，对应的继电器常开触点断开，常闭触点闭合。
//线圈状态（高字节）为其他值时，继电器状态保持不变
func (m *modbusRTUClient) On(branch byte) error {
	return m.one(uint16(branch), 0xff00)
}

func (m *modbusRTUClient) OffAll() error {
	m.locker.Lock()
	defer m.locker.Unlock()
	results, err := m.client.WriteMultipleCoils(0, 16, []byte{0, 0})
	if err != nil {
		return err
	}
	if len(results) == 2 {
		return nil
	}
	return ErrDateLength
}

func (m *modbusRTUClient) Off(branch byte) error {
	return m.one(uint16(branch), 0x0000)
}

func (m *modbusRTUClient) Status() ([]byte, error) {
	m.locker.Lock()
	defer m.locker.Unlock()
	results, err := m.client.ReadCoils(0, 16)
	if err != nil {
		return nil, err
	}
	if len(results) != 2 {
		return nil, ErrDateLength
	}
	return bin.Revert(bin.FromUint16(bit.ToUint16(bin.Revert(results)))), nil
}

func (m *modbusRTUClient) Info() (*RelayInfo, error) {
	m.locker.Lock()
	defer m.locker.Unlock()
	results, err := m.client.ReadHoldingRegisters(0, 15)
	if err != nil {
		return nil, err
	}
	if len(results) != 30 {
		return nil, ErrDateLength
	}
	info := relayInfo(results).To()
	return &info, nil
}

// 继电器路数为 1 ~ 16 路
func (m *modbusRTUClient) one(branch, value uint16) error {
	if branch < 1 || branch > 16 {
		return errors.New("继电器路数范围为1~16")
	}
	m.locker.Lock()
	defer m.locker.Unlock()
	// 串口的继电器路数为0~15
	results, err := m.client.WriteSingleCoil(branch-1, value)
	if err != nil {
		return err
	}
	if bit.ToUint16(results) == value {
		return nil
	}
	return ErrDateLength
}

func (m *modbusRTUClient) Close() {
	err := m.handler.Close()
	if err != nil {
		return
	}
}
